%include "src/boot/pm.inc"

PageDirBase0        equ 00200000h ; 页目录开始地址:  2M
PageTblBase0        equ 00201000h ; 页表开始地址:    2M +  4K

;ORG 0x7e00
ORG 0x90000
jmp LABEL_START
nop
LABEL_GDT_ADDR:             Descriptor 0, 0, 0 ;LABEL_GDT_ADDR=0x90003
LABEL_DESC_FLAT_C:          Descriptor 0, 0xFFFFF, DA_CR | DA_32 | DA_LIMIT_4K ;0~4G 存在的可执行可读代码段属性值
LABEL_DESC_FLAT_RW:         Descriptor 0, 0xFFFFF, DA_DRW | DA_32 | DA_LIMIT_4K
LABEL_DESC_VIDEO:           Descriptor 0xB8000, 0xFFFF, DA_DRW | DA_DPL3

GdtLen equ $ - LABEL_GDT_ADDR ;GDT长度
GdtPtr:
dw GdtLen - 1 ;GDT界限
dd LABEL_GDT_ADDR  ;GDT基地址

;GDT选择子
SelectorVideo           equ LABEL_DESC_VIDEO - LABEL_GDT_ADDR
SelectorFlatC           equ (LABEL_DESC_FLAT_C - LABEL_GDT_ADDR)
SelectorFlatRW          equ (LABEL_DESC_FLAT_RW - LABEL_GDT_ADDR)

LABEL_SEG_DATA:
CursorPos:           dd 160 ;共25行，80列 (80*row+col)*2=160*row+2*col  max=160*24+80*2 = 4000   初始值在第1行，第0行用户boot及loader16保护模式输出
TextAttr:           db 0xf
                    db 0x0, 0x0, 0x0 ;align 4
MemChkBuf:       times 512 db 0
MemInfoNum:       dd 0
TotolMemSize:       dd 0
MaxMemSize:       dd 0
PageTableNumber: dd  0

PMMessage: db "In Protected Mode now ~_~", 0xa, 0
SMessage: db "hello world!", 0xa, 0
Str0x: db "0x", 0
StrMemInfoTitle: db "------mem info------", 0xa,
                 db "Addr-Low   Addr-High  Size-Low   Size-High  Type(1:usable)", 0xa, 0
StrTotolSize: db "Totol Mem Size:0x", 0
StrMaxSize: db "Max Avalable Mem Addr:0x", 0
StrByte: db " Byte", 0
StrNeedPagingTableCount: db "Need PDE:0x", 0

get_mem_info: ;void (*)(void)
        push es
        mov ax, 0x9000
        mov es, ax
        push ecx

        mov ecx, 20 ;es:di所指向的地址范围描述符结构的大小， 以字节为单位。
                    ;无论es:di所指向的结构如何设置， BIOS最多将会填充ecx个字节。
                    ;不过， 通常情况下无论ecx为多大， BIOS只填充20字节， 有些BIOS忽略ecx的值， 总是填充20字节。
        mov ebx, 0  ;ebx 放置着“后续值（continuation value）” ，第一次调用时ebx必须为0
        mov edi, MemChkBuf
    gmi_loop:
        mov eax, 0xE820
        mov edx, 0x534D4150 ;（'SMAP'） ──BIOS将会使用此标志， 对调用者将要请求的系统映像信息进行校验， 这些信息会被BIOS放置到es:di所指向的结构中
        int 0x15
        jc gmi_failed
        add edi, 20
        inc dword [MemInfoNum]
        cmp ebx, 0
        jne gmi_loop
        jmp gmi_fin
    gmi_failed:
        mov dword [MemInfoNum], 0

        mov ax, 0xb800
        mov gs, ax
        mov ah, 0x8C
        mov al, 'E'
        mov [gs:(80*0+10)*2], ax
        jmp $

    gmi_fin:
        mov ax, 0xb800
        mov gs, ax
        mov ah, 0x0A
        mov al, 'l'
        mov [gs:(80*0+10)*2], ax

        pop ecx
        pop es
        ret

prepare_for_prtect_mode: ;void (*)(void)
    lgdt [GdtPtr] ;加载GDTR
    cli ;关中断

    ;open A20
    in al, 0x92
    or al, 010b
    out 0x92, al
    ret

disk_address_packet:    db    0x10        ; [ 0] Packet size in bytes.
    db    0        ; [ 1] Reserved, must be 0.
    db    1        ; [ 2] Nr of blocks to transfer.
    db    0        ; [ 3] Reserved, must be 0.
    dw    0        ; [ 4] Addr of transfer - Offset
    dw    0x8000    ; [ 6] buffer.          - Seg
    dd    0        ; [ 8] LBA. Low  32-bits.
    dd    0        ; [12] LBA. High 32-bits.

; read_sector
;----------------------------------------------------------------------------
; Entry:
;     - fields disk_address_packet should have been filled
;       before invoking the routine
; Exit:
;     - es:bx -> data read
; registers changed:
;     - //eax, ebx, dl, si, es
read_sector:
    push eax
    push ebx
    push edx
    push esi


    xor    ebx, ebx
    mov    ah, 0x42
    mov    dl, 0x80
    mov    si, disk_address_packet
    int    0x13
    ;jc  failed2 ; retry        ; 如果读取错误 CF 会被置为 1, 这时就不停地读, 直到正确为止
    jmp succ2
failed2:
    mov ah, 0x8C
    mov al, 'E'
    mov [gs:(80*0+9)*2], ax
    jmp $
succ2:
    ;mov    ax, [disk_address_packet + 6]
    ;mov    es, ax
    ;mov    bx, [disk_address_packet + 4]
    pop esi
    pop edx
    pop ebx
    pop eax
    ret

load_kernel: ;void (*)(void)
; 将kenel.bin从磁盘的
;      addr-range           len                 content        sector-range     sector-count
; [ 0xf200  - 0x100000)     0xf0e00(963.5k)     kernel.bin     [121, 2047]      1927
; copy到内存的
; [0x80000 - 0x90000)  0x10000(64k)位置
    push bp
    mov bp, sp
    push si
    push di
    push es

    mov ax, 0x8000
    mov es, ax

    mov si, 121 ;sect_no
    mov di, 0 ;to
    read_sect_loop:
    cmp si, (121 + 64 * 2) ;64k
    jz read_sect_fin
    ;push di
    ;push si
    ;call read_1sector ;void (*)(short sect_no, void* to) ;注意:这里认为sect_no从0开始，取值区间为[0, 2047] ????

    mov [disk_address_packet + 4], di ;to
    mov [disk_address_packet + 8], esi ;sect_no
    call read_sector
    ;add sp, 4

    add si, 1
    add di, 512
    jmp read_sect_loop

read_sect_fin:
    mov ax, 0xb800
    mov gs, ax
    mov ah, 0x0A
    mov al, 'k'
    mov [gs:(80*0+9)*2], ax

    pop es
    pop di
    pop si
    leave
    ret

LABEL_START:
    mov ax, 0xb800
    mov gs, ax
    mov ah, 0x0A
    mov al, 'L'
    mov [gs:(80*0+8)*2], ax

    mov ax, cs  ;cs=0x9000
    mov ds, ax  ;ds=0x9000 之所以这样做是因为，目前的数据和代码是混在一块的（未分段），即便是分段的也是在同一个段中
    mov es, ax
    mov ax, 0x0
    mov ss, ax
    mov sp, 0x7c00 ;ss:sp -> 0x7c00

    call load_kernel
    call get_mem_info

    call prepare_for_prtect_mode

    mov ax, 0xb800
    mov gs, ax
    mov ah, 0x0A
    mov al, 'n'
    mov [gs:(80*0+11)*2], ax

    mov eax, cr0
    or eax, 1    ;set protected mode bit
    mov cr0, eax

    jmp dword SelectorFlatC:LABEL_SEG_CODE32 ;enter protected mode, cs<-SelectorCode32 eip<-LABEL_SEG_CODE32

[SECTION .s32]
ALIGN   32
[BITS   32]
LABEL_SEG_CODE32:
    mov ax, SelectorVideo
    mov gs, ax

    mov ax, SelectorFlatRW
    mov es, ax
    mov fs, ax
    mov ds, ax
    mov ss, ax
    mov esp, TopOfStack
    and esp, 0xFFFFFFFC

    ;call clear_screen

    push PMMessage;In Protected Mode now ~_~
    call show_msg ;void (*)(char* s)
    add esp, 4

    add dword [MemInfoNum], 1

    push dword [MemInfoNum]
    call print_int ;void (*)(int i)
    add esp, 4

    push dword [MemInfoNum]
    push MemChkBuf ;must be ds's offset
    call print_mem ;void (*)(int start_addr, int item_count)
    add esp, 4 * 2

    call    setup_paging     ; 启动分页

    push SMessage ;hello world!
    call show_msg
    add esp, 4

    call load_kernel_prog
    mov ax, SelectorFlatRW
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov ss, ax

    jmp dword SelectorFlatC:0x30400

;typedef struct {
;  unsigned char e_ident[16]; /* Magic number and other info */ 0
;  u16    e_type;         /* Object file type */ 16
;  u16    e_machine;      /* Architecture */ 18
;  u32    e_version;      /* Object file version */ 20
;  u32    e_entry;        /* Entry point virtual address */24
;> u32    e_phoff;        /* Program header table file offset */28
;  u32    e_shoff;        /* Section header table file offset */32
;  u32    e_flags;        /* Processor-specific flags */36
;  u16    e_ehsize;       /* ELF header size in bytes */40
;> u16    e_phentsize;        /* Program header table entry size */42
;> u16    e_phnum;        /* Program header table entry count */44
;  u16    e_shentsize;        /* Section header table entry size */
;  u16    e_shnum;        /* Section header table entry count */
;  u16    e_shstrndx;     /* Section header string table index */
;} Elf32_Ehdr;

;typedef struct {
;  u32    p_type;         /* Segment type */ 0
;  u32    p_offset;       /* Segment file offset */4
;  u32    p_vaddr;        /* Segment virtual address */8
;  u32    p_paddr;        /* Segment physical address */12
;  u32    p_filesz;       /* Segment size in file */16
;  u32    p_memsz;        /* Segment size in memory */20
;  u32    p_flags;        /* Segment flags */24
;  u32    p_align;        /* Segment alignment */28
;} Elf32_Phdr;

load_1kernel_seg: ;void (*)(Elf32_Phdr* p_prog_item)
    push ebp
    mov ebp, esp

    mov eax, ds
    push eax
    mov ax, SelectorFlatRW
    mov ds, ax

    mov edx, [ebp + 8] ;p_prog_item
    mov eax, [es:edx + 16] ; Segment size in file=len
    mov ecx, [es:edx + 20] ; Segment size in memory
    sub ecx, eax ;ecx=p_memsz - p_filesz=bss len
    push ecx
    push eax

    ;mov edx, [ebp + 8] ;p_prog_item
    mov eax, [es:edx + 4] ; Segment file offset=src
    add eax, 0x80000
    push eax

    ;mov edx, [ebp + 8] ;p_prog_item
    mov eax, [es:edx + 8] ; Segment virtual address=dst
    push eax

    call mem_cpy ; void (*)(char* dst, char* src, int len)
    mov eax, [esp] ;dst
    mov edx, [esp + 8] ;file-size
    add eax, edx ;eax=dst+file-size=bss start address
    add esp, 3 * 4

    pop ecx
    push ecx
    push 0x0
    push eax
    call mem_set ; void (*)(char* dst, char c, int len)
    add esp, 3 * 4

    pop eax
    mov ds, ax

    leave
    ret

load_kernel_prog: ;void (*)(void)
    push ebp
    mov ebp, esp
    push esi
    push edi

    mov ax, SelectorFlatRW
    mov es, ax

    mov esi, [es:0x80000 + 28] ;e_phoff
    add esi, 0x80000
    ;movzx edx, word [es:0x80000 + 42] ;e_phentsize always=32
    movzx edi, word [es:0x80000 + 44] ;e_phnum

    lkp_loop:
        cmp edi, 0
        jz lkp_fin

        ;if (1 != pElf32_Phdr->p_type) continue
        mov eax, [es:esi] ;pElf32_Phdr->p_type
        cmp eax, 1
        jnz continue_item
            push esi
            call load_1kernel_seg
            add esp, 4
        continue_item:
        add esi, 32 ;+=e_phentsize
        sub edi, 1
        jmp lkp_loop
    lkp_fin:
    pop edi
    pop esi
    leave
    ret

mem_cpy: ; void (*)(char* dst, char* src, int len)
    push ebp
    mov ebp, esp
    push esi

    mov esi, [ebp + 8] ;dst
    mov edx, [ebp + 12] ;src
    mov ecx, [ebp + 16] ;len

    mc_loop:
    cmp ecx, 0
    jz mc_fin
        mov al, [edx] ;ds->faltRW
        mov [esi], al
        add edx,1
        add esi, 1
        sub ecx, 1
    jmp mc_loop
    mc_fin:

    pop esi
    leave
    ret

mem_set: ; void (*)(char* dst, char c, int len)
    push ebp
    mov ebp, esp

    mov eax, [ebp + 8] ;dst
    movzx edx, byte [ebp + 12] ;c
    mov ecx, [ebp + 16] ;len

    ms_loop:
    cmp ecx, 0
    jz ms_fin
        mov [eax], dl
        add eax, 1
        sub ecx, 1
    jmp ms_loop
    ms_fin:

    leave
    ret

set_pd: ;void (*)(void* phy_pd_base, void* phy_pt_base, int pt_count) note:es was Scratched
    push ebp
    mov ebp, esp
    push edi

    mov ax, SelectorFlatRW
    mov es, ax

    mov eax, [ebp+12]
    or eax, PG_P  | PG_USU | PG_RWW
    mov edi, [ebp+8] ;edi->phy_pd_base=write_dest
    mov ecx, [ebp+16] ;ecx=pt_count
    l1:
        stosd ;[es:edi]=eax edi+=4
        add eax, 4096
    loop l1 ;if(--ecx) jmp l1

    pop edi
    leave
    ret

set_pt_item: ;void (*)(void* phy_pt_base, void* logic_addr, void* phy_page_frame)
    push ebp
    mov ebp, esp
    mov ax, SelectorFlatRW
    mov es, ax

    mov eax, [ebp+12] ;logic_addr
    shr eax, 22
    mov ecx, 4096
    mul ecx
    mov ecx, eax

    mov eax, [ebp+12] ;logic_addr
    shr eax, 12
    and eax, 0x3FF ;[0000 0000  00][00 0000  0000] 0000  0000 0000
    mov ebx, 4
    mul ebx
    add eax, ecx
    add eax, [ebp+8] ; pt_base
    mov ebx, [ebp+16];phi_page_frame
    or ebx, PG_P | PG_USU | PG_RWW
    mov [es:eax], ebx

    leave
    ret

clear_screen: ;void (*)(void)
    mov ecx, 0 ;max_row=25 max_col=80  共25行，80列 160*row+2*col  max=160*24+80*2 = 4000  =>[0, 3999]
    mov al, 0
    mov ah, TA_TEXT_WHITE
    c_s_loop:
        cmp ecx, 4000
        jz c_s_fin
        mov [gs:ecx], ax
        add ecx, 2
    jmp c_s_loop
    c_s_fin:
    ret

TA_FOREGROUND_BLUE      equ (0x01)
TA_FOREGROUND_GREEN     equ (0x02)
TA_FOREGROUND_RED       equ (0x04)
TA_FOREGROUND_INTESITY  equ (0x08)
TA_BACKGROUND_BLUE      equ (0x10)
TA_BACKGROUND_GREEN     equ (0x20)
TA_BACKGROUND_RED       equ (0x40)
TA_BLINKING             equ (0x80)
TA_TEXT_WHITE_B            equ (TA_FOREGROUND_BLUE | TA_FOREGROUND_GREEN | TA_FOREGROUND_RED | TA_FOREGROUND_INTESITY)
TA_TEXT_WHITE           equ (TA_FOREGROUND_BLUE | TA_FOREGROUND_GREEN | TA_FOREGROUND_RED)

show_msg: ;void (*)(char* s)
        push ebp
        mov ebp, esp

        push esi
        push edi
        xor edi, edi
        xor esi, esi
        xor eax, eax

        mov esi, [ebp + 8] ;字串地址 注意：等同于 mov esi, [ss:ebp + 8]，时刻注意体会隐含的gdt
        mov ah, [TextAttr]
    show_msg_loop:
        mov al, [ds:esi]
        add esi, 1
        test al, al
        jz show_msg_fin
        movzx eax, al
        push eax
        call print_char
        add esp, 4
        jmp show_msg_loop
    show_msg_fin:
        pop edi
        pop esi
        leave
        ret

print_char: ;void (*)(char c)
    push ebp
    mov ebp, esp

    mov ecx, [CursorPos]
    mov al, [ebp + 8] ;load char c
    cmp al, 0xa ;\n
    jz pc_0xa

    mov ah, [TextAttr]
    mov [gs:ecx], ax
    add ecx, 2
    mov [CursorPos], ecx
    jmp pc_fin

    pc_0xa:
    mov eax, [CursorPos]; is \n: row+=1
    xor edx, edx  ;edx=0
    mov edi, 160  ;edi=160
    div edi ;cursorpos/16 ax=row   ax=eax即CursorPos/160 16位数-取商，也就是行数
    add eax, 1  ;行+=1
    mul edi     ;ax *= 160
    mov [CursorPos], eax

    pc_fin:
    leave
    ret

print_char_hex: ;void (*)(char c) 'a'=>(0x)61
    push ebp
    mov ebp, esp

    ;high 4
    mov al, [ebp + 8] ;c
    shr al, 4 ;get high 4 to al
    cmp al, 9
    ja abcdef ;if al > 9
    add al, '0'
    jmp calc_fin
    abcdef:
    add al, 'A' - 10
    calc_fin:
    movzx eax, al
    push eax
    call print_char
    add esp, 4

    ;low 4
    mov al, [ebp + 8] ;c
    and al, 0xf
    cmp al, 9
    ja abcdef2 ;if al > 9
    add al, '0'
    jmp calc_fin2
    abcdef2:
    add al, 'A' - 10
    calc_fin2:
    movzx eax, al
    push eax
    call print_char
    add esp, 4

    leave
    ret

print_int:  ;void (*)(int i)
    push ebp
    mov ebp, esp

    ;show [24:31]
    mov eax, [ebp + 8] ;i
    shr eax, 24
    movzx eax, al
    push eax
    call print_char_hex ;void (*)(char c)
    add esp, 4

    ;show [16:24]
    mov eax, [ebp + 8] ;i
    shr eax, 16
    movzx eax, al
    push eax
    call print_char_hex
    add esp, 4

    ;show [8:15]
    mov eax, [ebp + 8] ;i
    shr eax, 8
    movzx eax, al
    push eax
    call print_char_hex
    add esp, 4

    ;show [0:7]
    mov eax, [ebp + 8] ;i
    movzx eax, al
    push eax
    call print_char_hex
    add esp, 4

    leave
    ret

print_1mem_item: ;void (*)(int addr)
    push ebp
    mov ebp, esp
    push esi

    mov esi, 0 ;itoritor[0, 4]
    pi_loop:
        cmp esi, 5
        jz pi_loop_fin
        push Str0x
        ;call show_msg ;void (*)(char* s)
        add esp, 4

        mov eax, 4
        mul esi ;eax=4*itoritor[0, 4]
        add eax, [ebp+8];eax=addr+4*itoritor[0, 4]
        push dword [eax]
        ;call print_int ;void (*)(int i)
        add esp, 4

        ; add space
        ;add dword [CursorPos], 2
        add esi, 1
     jmp pi_loop
     pi_loop_fin:
        push 0xa
        ;call print_char
        add esp, 4

        ;if (1==type && len_l+base_l>max_mem) max_mem = len_l+base_l;
        mov eax, [ebp + 8]
        add eax, 4 * 4 ;&type
        mov eax, [eax] ;eax->type
        cmp eax, 1
        jnz calc_sum_mem_size

        mov eax, [ebp + 8]
        add eax, 4 * 2 ;&len_l
        mov ecx, [eax] ;ecx->len_l

        mov eax, [ebp + 8]
        ;add eax, 4 * 0 ;&base_l
        mov eax, [eax] ;edx->base_l

        add eax, ecx ;eax=base_l+len_l
        cmp eax, [MaxMemSize] ;if(base_l+len_l>max_mem_size) max_mem_size = base_l+len_l
        jbe calc_sum_mem_size
            mov dword [MaxMemSize], eax
        calc_sum_mem_size:
            mov eax, [ebp + 8]
            add eax, 4 * 2 ;&len_l
            mov eax, [eax] ;eax=len_l
            add dword [TotolMemSize], eax
    p1i_fin:
    pop esi
    leave
    ret

print_mem: ;void (*)(int start_addr, int item_count)
    push ebp
    mov ebp, esp
    push esi

    push StrMemInfoTitle
    call show_msg
    add esp, 4

    mov esi, 0 ;itoritor
    ptm_loop:
        mov eax, [ebp + 12]
        cmp esi, eax
        jz ptm_loop_fin

        mov eax, 4 * 5 ;mem_info_item len
        mul esi ;eax=20*itoritor[0, item_count)
        add eax, [ebp + 8] ;eax=start_addr+4*mem_info_item
        push eax
        call print_1mem_item
        add esp, 4

        add esi, 1
    jmp ptm_loop

    ptm_loop_fin:
        push StrTotolSize
        call show_msg
        add esp, 4

        push dword [TotolMemSize]
        call print_int
        add esp, 4

        push StrByte
        call show_msg
        add esp, 4

        push 0xa
        call print_char
        add esp, 4

        push StrMaxSize
        call show_msg
        add esp, 4

        push dword [MaxMemSize]
        call print_int
        add esp, 4

        push 0xa
        call print_char
        add esp, 4

        pop esi
        leave
        ret

;线性地址===物理地址时的pd/pt
;PageDirBase0=0x00200000=2M
;0x00200000 <+   0>:    0x00201027  0x00202007  0x00203007  0x00204007 .
;0x00200010 <+  16>:    0x00205007  0x00206007  0x00207007  0x00208007  |
;0x00200020 <+  32>:    0x00209007  0x0020a007  0x0020b007  0x0020c007  |
;0x00200030 <+  48>:    0x0020d007  0x0020e007  0x0020f007  0x00210007  |
;0x00200040 <+  64>:    0x00211007  0x00212007  0x00213007  0x00214007  |
;0x00200050 <+  80>:    0x00215007  0x00216007  0x00217007  0x00218007  |
;...                                                                    |--4k paging-dir, 参考［PDE 结构］
;0x002001e0 <+ 480>:    0x00279007  0x0027a007  0x0027b007  0x0027c007  |
;0x002001f0 <+ 496>:    0x0027d007  0x0027e007  0x0027f007  0x00280007  |
;0x00200200 <+ 512>:    0x00000000  0x00000000  0x00000000  0x00000000  |
;...                                                                    |
;0x00200ff0 <+4080>:    0x00000000  0x00000000  0x00000000  0x00000000 /

;0x00201000 <+   0>:    0x00000007  0x00001007  0x00002007  0x00003007 .
;0x00201010 <+  16>:    0x00004007  0x00005007  0x00006007  0x00007007  |
;0x00201020 <+  32>:    0x00008027  0x00009007  0x0000a007  0x0000b007  |
;0x00201030 <+  48>:    0x0000c007  0x0000d007  0x0000e007  0x0000f007  |
;...                                                                    |--1个4k paging-table,像这样的table共有1024个，
;0x00201fc0 <+4032>:    0x003f0007  0x003f1007  0x003f2007  0x003f3007  |  但由于我们实际内存大小是512M，计算可得只需要128个就足够了
;0x00201fd0 <+4048>:    0x003f4007  0x003f5007  0x003f6007  0x003f7007  |
;0x00201fe0 <+4064>:    0x003f8007  0x003f9007  0x003fa007  0x003fb007  |
;0x00201ff0 <+4080>:    0x003fc007  0x003fd007  0x003fe007  0x003ff007 /
;....

;以最简单的假设 Virtual Address是0为例，看下其映射后的物理地址是多少
;           _指示该虚拟地址在Page Dir Table中的Offset 所以第1步找到PageDirBase0+0=0x00200000位置，
;          | 数据为0x00201027，这里我们只关心PageTblBase=0x00201000,参考[PDE 结构]
;          |                       _指示该虚拟地址在Page Table Table中的Offset 第2步找到PageTblBase+0=0x00201000位置的数据0x00000007，
;          |                      | 这里我也只关心base=0x0，参考[PTE 结构]
;          |                      |                            _在PDE指示的4k页中，相对页基址的Offset,其寻址范围为[0, 0xFFF]即[0, 4095]正好可以
;          |                      |                           | 寻址4k范围,此例中为0，所以第3步，最终映射后的物理地址是0x0(base)+0x0(Offset)=0x0
;__________|__________________ ___|_________________________ _|______________________________
;31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 09 08 07 06 05 04 03 02 01 00
; 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

setup_paging:
    ; 根据内存大小计算应初始化多少PDE以及多少页表
    xor edx, edx
    ;mov eax, [MaxMemSize]
    mov eax, 0xFFFFFFFF;[MaxMemSize]
    mov ebx, 400000h    ; 400000h = 4M = 4096 * 1024, 一个页表对应的内存大小
    div ebx ;ax:商 dx:余数
    mov ecx, eax    ; 此时 ecx 为页表的个数，也即 PDE 应该的个数
    test    edx, edx
    jz  .no_remainder
    inc ecx     ; 如果余数不为 0 就需增加一个页表
.no_remainder:
    mov [PageTableNumber], ecx  ; 暂存页表个数

    ; 为简化处理, 所有线性地址对应相等的物理地址. 并且不考虑内存空洞.

    ; 首先初始化页目录
    mov ax, SelectorFlatRW
    mov es, ax
    mov edi, PageDirBase0   ; 此段首地址为 PageDirBase0
    xor eax, eax
    mov eax, PageTblBase0 | PG_P | PG_USU | PG_RWW
.1:
    stosd ;把EAX中的数据装入ES:EDI指向的存储单元，然后根据DF标志增减DI,直到ecx为0
    add eax, 4096       ; 为了简化, 所有页表在内存中是连续的.
    loop    .1 ;bochs中debug调试时按n会像函数一样执行整个loop循环序列

    ; 再初始化所有页表
    mov eax, [PageTableNumber]  ; 页表个数
    mov ebx, 1024       ; 每个页表 1024 个 PTE
    mul ebx
    mov ecx, eax        ; PTE个数 = 页表个数 * 1024
    mov edi, PageTblBase0   ; 此段首地址为 PageTblBase0
    xor eax, eax
    mov eax, PG_P  | PG_USU | PG_RWW
.2:
    stosd
    add eax, 4096       ; 每一页指向 4K 的空间
    loop    .2

    mov eax, PageDirBase0
    mov cr3, eax
    mov eax, cr0
    or  eax, 80000000h
    mov cr0, eax
    jmp short .3
.3:
    nop
    ret

set_cursor_pos: ;void (*)(short row, short col)
    push ebp
    mov ebp, esp

    ;(80 * row + col) * 2
    mov eax, 160
    mov ecx, [ebp + 8] ;row
    mul ecx ;eax=160*row
    mov edx, eax ;edx=160*row

    mov eax, 2
    mov ecx, [ebp + 12];eax=2*col
    add eax, edx ;eax=160*row+2*col
    mov [CursorPos], eax

    leave
    ret

get_cursor_pos: ;int (*)(void) ret: high16=row, low16=col
    push ebp
    mov ebp, esp
    push esi

;字操作：32位被除数放在DX,AX中。其中DX为高位字，16位除数为源操作数，结果的16位端在AX中，16位余数在DX中。表示为
;(AX)<-(DX,AX)/(SRC) 的商
;(DX)<-(DX,AX)/(SRC) 的余数
    ;160row + 2col
    mov edx, 0
    mov eax, [CursorPos]
    mov ebx, 160
    div ebx ;now ax=商(row) dx=余数(col)
    shl eax, 16
    mov esi, eax ;restore to esi
    mov eax, edx
    mov edx, 0
    mov ebx, 2
    div ebx ;eax=2col/2=col
    and eax, 0xFFFF
    or eax, esi

    pop esi
    leave
    ret

set_text_attr: ;void (*)(char attr)
    mov eax, [esp + 4]
    mov [TextAttr], al
    ret
get_text_attr: ;char (*)(void)
    xor eax, eax
    mov al, [TextAttr]
    ret
; section .s32 end

StackSpace: times   1000h   db  0
TopOfStack  equ $ ; 栈顶
